﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace XNAViewerSample.Forms
{
    #region Delegates
    public delegate List<String> ModelLoadHandler(String ID);
    public delegate void ModelClothTypeHandler(String hook, SelectedModel.mtype newtype, SelectedModel.mtype oldtype);
    public delegate void ModelHairTypeHandler(String hook, SelectedModel.htype newtype, SelectedModel.htype oldtype);
    public delegate List<String> ModelClothHandler(String hook, bool toLoad, SelectedModel.mtype mType, SelectedModel.ctype cType, String value);
    public delegate List<String> ModelAnimationHandler(String hook, bool toLoad, String newValue, String oldValue);
    public delegate List<String> ModelAccessoryHandler(String hook, bool toLoad, SelectedModel.atype type, String value);
    public delegate List<String> ModelDanceHandler(String hook, String newValue, String oldValue);
    public delegate List<String> ModelMorphHandler(String hook, bool toLoad, String newValue, String oldValue);
    public delegate List<String> ModelVisemeHandler(String hook, String newValue, String oldValue);
    public delegate void VoiceOptionHandler(byte options);
    public delegate void ChatInputHandler(string text);
    #endregion

    public partial class Control_Panel : Form
    {
        #region Fields
        private List<String> modelList;
        private SelectedModel objModel;
        private byte voiceOptions = 0;
        private const byte optVoice = 0x1;
        private const byte optVolume = 0x2;
        private const byte optRate = 0x4;
        private const byte optPitch = 0x8;

        private bool newRequest;
        private string navToUrl;
        private string navToText;
        private string logText;

        #endregion

        #region Events
        public event ModelLoadHandler modelLoadHandler;
        public event ModelClothTypeHandler modelClothTypeHandler;
        public event ModelHairTypeHandler modelHairTypeHandler;
        public event ModelClothHandler modelClothHandler;
        public event ModelAnimationHandler modelAnimationHandler;
        public event ModelAccessoryHandler modelAccessoryHandler;
        public event ModelDanceHandler modelDanceHandler;
        public event ModelMorphHandler modelMorphHandler;
        public event ModelVisemeHandler modelVisemeHandler;
        public event VoiceOptionHandler voiceOptionHandler;
        public event ChatInputHandler chatInputHandler;
        #endregion

        public Control_Panel()
        {
            InitializeComponent();
        }

        #region Get/Set Methods
        public Boolean InputFocused
        {
            get
            {
                if (tabControl.Focused) return true;
                if (this.Focused) return true;
                return false;
            }
        }
        public string Voice
        {
            get { return (string)this.comboBox1.SelectedItem; }
            set
            {
                if (this.comboBox1.Items.Contains(value))
                    this.comboBox1.SelectedIndex = this.comboBox1.Items.IndexOf(value);
                else
                    this.comboBox1.SelectedIndex = 0;
            }
        }
        public int Volume
        {
            get { return this.trackBar1.Value; }
            set { this.trackBar1.Value = value; }
        }
        public int Rate
        {
            get { return this.trackBar2.Value; }
            set { this.trackBar2.Value = value; }
        }
        public int Pitch
        {
            get { return this.trackBar3.Value; }
            set { this.trackBar3.Value = value; }
        }
        public string LogText
        {
            get { return logText; }
        }

        #endregion

        #region Helpers
        public Boolean AllowMixed()
        {
            return objModel.AllowMixed;
        }
        private String GetModelHook()
        {
            return this.modelTextBox.Text;
        }
        public bool AcceptDrop()
        {
            return (!String.IsNullOrEmpty(objModel.ID) && (objModel.Animation.Contains("Beat")));
        }
        public void AddVoice(String voiceName)
        {
            this.comboBox1.Items.Add(voiceName);
        }
        public int GetVoiceIndex()
        {
            return this.comboBox1.SelectedIndex;
        }
        public void UpdateProgress(String text, float progress)
        {
            if (!String.IsNullOrWhiteSpace(text))
                this.toolStripStatusLabel1.Text = text;
            this.toolStripProgressBar1.Value = (int)(progress * 100);
            Application.DoEvents();
        }
        public void UpdateDance(String ID)
        {
            objModel.Dance = ID;

            // invalidate control to force an update
            this.modelPropertyGrid.Invalidate();
            Application.DoEvents();
        }
        public void EnableChat(Boolean enable)
        {
            if (!enable)
            {
                this.textInput.Enabled = false;
                this.comboBox1.Enabled = false;
                this.trackBar1.Enabled = false;
                this.trackBar2.Enabled = false;
                this.trackBar3.Enabled = false;
                this.button1.Enabled = false;
            }
            else
            {
                this.textInput.Enabled = true;
                this.comboBox1.Enabled = true;
                this.trackBar1.Enabled = true;
                this.trackBar2.Enabled = true;
                this.trackBar3.Enabled = true;
                this.button1.Enabled = true;
            }
        }

        public XNAViewer.ResponseType AddLog(string log, bool request)
        {
            if (request)
            {
                this.listLog.SelectionColor = Color.LimeGreen;
                this.listLog.SelectionFont = new Font(listLog.Font, listLog.Font.Style | FontStyle.Italic);
            }
            else
            {
                this.listLog.SelectionColor = Color.Blue;
                this.listLog.SelectionFont = new Font(listLog.Font, listLog.Font.Style | FontStyle.Bold);
            }

            // parse HTML items
            XNAViewer.ResponseType type = ParseLog(log);
            if (type == XNAViewer.ResponseType.TEXT)
                logText = ReplaceHTML(log);
            else
                logText = navToText;

            this.listLog.SelectedText = logText + Environment.NewLine;
            return type;
        }

        private string ReplaceHTML(string log)
        {
            string newLog = log;
            if (log.Contains("<br/>"))
                newLog = log.Replace("<br/>", "\n");
            if (log.Contains("&lt;br/&gt;"))
                newLog = log.Replace("&lt;br/&gt;", "\n");
            return newLog;
        }

        private XNAViewer.ResponseType ParseLog(string log)
        {
            /*
            if (log.Contains("<html>") || log.Contains("&lt;html&gt;"))
            {
                // decode navToUrl
                if (log.Contains("Google Search"))
                    navToText = "Searching in Google.";
                if (log.Contains("Wikipedia Search"))
                    navToText = "Looking for Wikipedia entry.";
                if (log.Contains("Dmoz Search"))
                    navToText = "Searching Open Directory";
                string[] split = log.Split(new char[] { '"' });
                string fixedUrl = split[1].Replace(' ', '+');
                navToUrl = fixedUrl;
                return XNAViewer.ResponseType.BROWSER;
            }
            if (log.Contains("<system>") || log.Contains("&lt;system&gt;"))
            {
                // decode type
                if (log.Contains("MediaPlayer"))
                {
                    navToText = "Your DJ is in the house.";
                    string[] split = log.Split(new char[] { '"' });
                    playList = split[1];
                    sysType = XNAvatar.SystemType.MEDIA;
                }
                return XNAViewer.ResponseType.SYSTEM;
            }
            */
            return XNAViewer.ResponseType.TEXT;
        }

        #endregion

        #region Controls Methods
        private void Control_Panel_Load(object sender, EventArgs e)
        {
            // Get model list
            if (this.modelLoadHandler != null)
                modelList = this.modelLoadHandler(String.Empty);
            ListConverter.SetList("ID", modelList); 
            objModel = new SelectedModel();

            this.modelPropertyGrid.SelectedObject = objModel;
            // Disable other controls first
            UpdateProgress("", 0.0f);
            this.modelTextBox.Enabled = false;

        }

        private void modelPropertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            if (e.ChangedItem.Label == "ID")
            {
                this.modelTextBox.Text = (String)e.ChangedItem.Value;
                this.modelTextBox.Enabled = true;
                Application.DoEvents();
                if (this.modelLoadHandler != null)
                    this.modelLoadHandler((String)e.ChangedItem.Value);

                // place settings to back to default
                objModel.ClothingType = SelectedModel.mtype.Normal;
                objModel.HairType = SelectedModel.htype.Normal;

                // then populate cloth contents
                objModel.AllowMixed = false;

                List<String> newList = new List<string>();
                Application.DoEvents();
                if (modelClothHandler != null)
                    newList = modelClothHandler(this.GetModelHook(), false, objModel.ClothingType, SelectedModel.ctype.Tops, String.Empty);
                ListConverter.SetList(SelectedModel.ctype.Tops.ToString(), newList);
                objModel.Tops = "Default";

                Application.DoEvents();
                if (modelClothHandler != null)
                    newList = modelClothHandler(this.GetModelHook(), false, objModel.ClothingType, SelectedModel.ctype.Bottom, String.Empty);
                ListConverter.SetList(SelectedModel.ctype.Bottom.ToString(), newList);
                objModel.Bottom = "Default";

                Application.DoEvents();
                if (modelClothHandler != null)
                    newList = modelClothHandler(this.GetModelHook(), false, objModel.ClothingType, SelectedModel.ctype.Skirt, String.Empty);
                ListConverter.SetList(SelectedModel.ctype.Skirt.ToString(), newList);
                objModel.Skirt = "Default";

                // populate accessories
                // TODO: for now, only supports feet
                Application.DoEvents();
                if (modelAccessoryHandler != null)
                    newList = modelAccessoryHandler(this.GetModelHook(), false, SelectedModel.atype.Feet, String.Empty);
                ListConverter.SetList(SelectedModel.atype.Feet.ToString(), newList);
                objModel.Feet = "Off";

                // populate imports
                Application.DoEvents();
                if (modelAnimationHandler != null)
                    newList = modelAnimationHandler(this.GetModelHook(), false, String.Empty, String.Empty);
                newList.Insert(0, "Default");
                ListConverter.SetList("Animation", newList);
                objModel.Animation = "Default";

                Application.DoEvents();
                if (modelDanceHandler != null)
                    newList = modelDanceHandler(this.GetModelHook(), String.Empty, String.Empty);
                ListConverter.SetList("Dance", newList);
                objModel.Dance = "List of moves";

                Application.DoEvents();
                //NB: model need to be loaded first to get morphs
                //if (modelMorphHandler != null)
                //    newList = modelMorphHandler(this.GetModelHook(), false, String.Empty, String.Empty);
                //ListConverter.SetList("Expression", newList);
                objModel.Expression = "Base";

                Application.DoEvents();
                //NB: model need to be loaded first to get visemes
                objModel.Visemes = "List of visemes";

                // invalidate control to force an update
                this.modelPropertyGrid.Invalidate();

            }
            if (e.ChangedItem.Label == "ClothingType")
            {
                Application.DoEvents();
                String hook = this.GetModelHook();
                if (this.modelClothTypeHandler != null)
                    this.modelClothTypeHandler(hook, 
                                               (SelectedModel.mtype)e.ChangedItem.Value, 
                                               (SelectedModel.mtype)e.OldValue);

                // update clothing list
                List<String> newList = new List<string>();
                Application.DoEvents();
                if (modelClothHandler != null)
                    newList = modelClothHandler(this.GetModelHook(), false, objModel.ClothingType, SelectedModel.ctype.Tops, String.Empty);
                ListConverter.SetList(SelectedModel.ctype.Tops.ToString(), newList);
                objModel.Tops = "Default";

                Application.DoEvents();
                if (modelClothHandler != null)
                    newList = modelClothHandler(this.GetModelHook(), false, objModel.ClothingType, SelectedModel.ctype.Bottom, String.Empty);
                ListConverter.SetList(SelectedModel.ctype.Bottom.ToString(), newList);
                objModel.Bottom = "Default";

            }
            if (e.ChangedItem.Label == "HairType")
            {
                Application.DoEvents();
                if (this.modelHairTypeHandler != null)
                    this.modelHairTypeHandler(this.GetModelHook(),
                                              (SelectedModel.htype)e.ChangedItem.Value,
                                              (SelectedModel.htype)e.OldValue);
            }

            // CLothing
            if (e.ChangedItem.Label == "AllowMixed")
            {
                List<String> newList = new List<string>();
                Application.DoEvents();
                if (modelClothHandler != null)
                    newList = modelClothHandler(this.GetModelHook(), false, objModel.ClothingType, SelectedModel.ctype.Tops, String.Empty);
                ListConverter.SetList(SelectedModel.ctype.Tops.ToString(), newList);
                objModel.Tops = "Default";

                Application.DoEvents();
                if (modelClothHandler != null)
                    newList = modelClothHandler(this.GetModelHook(), false, objModel.ClothingType, SelectedModel.ctype.Bottom, String.Empty);
                ListConverter.SetList(SelectedModel.ctype.Bottom.ToString(), newList);
                objModel.Bottom = "Default";
            }

            if (e.ChangedItem.Label == SelectedModel.ctype.Tops.ToString())
            {
                Application.DoEvents();
                if (this.modelClothHandler != null)
                    this.modelClothHandler(this.GetModelHook(), true,
                                           objModel.ClothingType,
                                           SelectedModel.ctype.Tops,
                                           (String)e.ChangedItem.Value);
            }
            if (e.ChangedItem.Label == SelectedModel.ctype.Bottom.ToString())
            {
                Application.DoEvents();
                if (this.modelClothHandler != null)
                    this.modelClothHandler(this.GetModelHook(), true,
                                           objModel.ClothingType,
                                           SelectedModel.ctype.Bottom,
                                           (String)e.ChangedItem.Value);
            }
            if (e.ChangedItem.Label == SelectedModel.ctype.Skirt.ToString())
            {
                Application.DoEvents();
                if (this.modelClothHandler != null)
                    this.modelClothHandler(this.GetModelHook(), true,
                                           objModel.ClothingType,
                                           SelectedModel.ctype.Skirt, 
                                           (String)e.ChangedItem.Value);
            }

            // Accessories
            if (e.ChangedItem.Label == SelectedModel.atype.Feet.ToString())
            {
                Application.DoEvents();
                if (this.modelAccessoryHandler != null)
                    this.modelAccessoryHandler(this.GetModelHook(), true,
                                            SelectedModel.atype.Feet,
                                            (String)e.ChangedItem.Value);
            }

            // Imports
            if (e.ChangedItem.Label == "Animation")
            {
                Application.DoEvents();
                String hook = this.GetModelHook();
                if (this.modelAnimationHandler != null)
                    this.modelAnimationHandler(hook, true,
                                               (String)e.ChangedItem.Value,
                                               (String)e.OldValue);
            }

            if (e.ChangedItem.Label == "Expression")
            {
                Application.DoEvents();
                String hook = this.GetModelHook();
                if (this.modelMorphHandler != null)
                    this.modelMorphHandler(hook, true,
                                           (String)e.ChangedItem.Value,
                                           (String)e.OldValue);
            }
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            voiceOptions |= optVoice;
        }
        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            voiceOptions |= optVolume;
        }
        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            voiceOptions |= optRate;
        }
        private void trackBar3_Scroll(object sender, EventArgs e)
        {
            voiceOptions |= optPitch;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (voiceOptionHandler != null)
                voiceOptionHandler(voiceOptions);
            voiceOptions = 0x0;
        }
        private void textInput_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (newRequest)
            {
                textInput.Clear();
                newRequest = false;
            }
            if (e.KeyChar == 0x0D)  // Keys.Enter
            {
                textInput.SelectAll();
                if (chatInputHandler != null)
                    chatInputHandler(textInput.Text);
                newRequest = true;
            }
        }

        private void listLog_TextChanged(object sender, EventArgs e)
        {
            this.listLog.ScrollToCaret();
        }
        #endregion


    }
}
